package bma.common.netty.client;

import bma.common.langutil.ai.AIUtil;
import bma.common.langutil.ai.stack.AIStack;
import bma.common.langutil.ai.stack.AIStackOne4Timer;
import bma.common.langutil.ai.stack.AIStackWrap;
import bma.common.langutil.concurrent.TimerManager;

public abstract class CommonNettyClientSender<RESP_TYPE> implements
		NettyClientSender {

	protected AIStack<Boolean> sessionStack;
	protected AIStack<RESP_TYPE> responseStack;

	protected TimerManager timer;
	protected long timeout;

	public CommonNettyClientSender(AIStack<RESP_TYPE> responseStack) {
		super();
		this.responseStack = responseStack;
	}

	public CommonNettyClientSender(AIStack<RESP_TYPE> responseStack,
			long timeout, TimerManager timer) {
		super();
		this.responseStack = responseStack;
		this.timeout = timeout;
		this.timer = timer;
	}

	public TimerManager getTimer() {
		return timer;
	}

	public void setTimer(TimerManager timer) {
		this.timer = timer;
	}

	public long getTimeout() {
		return timeout;
	}

	public void setTimeout(long timeout) {
		this.timeout = timeout;
	}

	@Override
	public void beginSend(AIStack<Boolean> stack, NettyClient client) {
		this.sessionStack = stack;
		try {
			bindNettyClient(client, true);
			if (this.timeout > 0) {
				AIStackOne4Timer<RESP_TYPE> tstack = new AIStackOne4Timer<RESP_TYPE>(
						this.responseStack);
				this.responseStack = tstack;

				TimerManager tm = TimerManager.checkInstance(this.timer);
				tm.postTimerTask(tstack.createTimeout("timeout", timeout));
			}
			sendRequest(client);
		} catch (Exception e) {
			end(client, null, e);
		}
	}

	public void bindNettyClient(NettyClient client, boolean bind) {

	}

	public abstract void sendRequest(NettyClient client);

	@Override
	public void endSend(NettyClient client) {
		bindNettyClient(client, false);
	}

	public AIStack<RESP_TYPE> subStack(final NettyClient client) {
		return new AIStackWrap<RESP_TYPE>(responseStack) {

			@Override
			public boolean success(RESP_TYPE result) {
				return end(client, result, null);
			}
		};
	}

	public boolean end(NettyClient client, RESP_TYPE result, Throwable t) {
		if (t != null) {
			if (sessionStack != null) {
				AIUtil.safeFailure(sessionStack, t);
			}
			return AIUtil.safeFailure(responseStack, t);
		} else {
			if (sessionStack != null) {
				AIUtil.safeSuccess(sessionStack, true);
			}
			return AIUtil.safeSuccess(responseStack, result);
		}
	}

	@Override
	public boolean exceptionCaught(NettyClient client, Throwable t) {
		end(client, null, t);
		return false;
	}

	@Override
	public void clientClose(NettyClient client, boolean remoteClose) {
		AIUtil.safeFailure(responseStack, new NettyClientCloseException(
				remoteClose ? "channel remote close" : "channel close"));
	}
}
